package com.wyy.util;

import com.wyy.constant.Constant;
import com.wyy.error.BusinessException;
import com.wyy.error.EnumBusinessError;
import com.wyy.pojo.model.UserToken;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.MalformedClaimException;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.keys.HmacKey;
import org.jose4j.lang.JoseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.Key;

/**
 * @author: 吴磊
 * @program: blog
 * @create: 2021-04-19 21:49
 */
@Component
public class TokenUtils {

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 生成Token
     *
     * @param userToken
     * @return
     * @throws Exception
     */
    public String generateToken(UserToken userToken) throws UnsupportedEncodingException, JoseException {
        JwtClaims claims = new JwtClaims();
        claims.setSubject(userToken.getUserId());
        claims.setClaim(Constant.CONTEXT_NICK_NAME, userToken.getUserNickname());
        claims.setClaim(Constant.CONTEXT_IMAGE, userToken.getUserProfilePhoto());
        claims.setClaim(Constant.CONTEXT_DATE, userToken.getUserRegistrationTime());
        //设置token的过期时间为1天，redis中也是一天
        claims.setExpirationTimeMinutesInTheFuture(60 * 24);

        Key key = new HmacKey(Constant.JWT_PRIVATE_KEY.getBytes("UTF-8"));

        JsonWebSignature jws = new JsonWebSignature();
        jws.setPayload(claims.toJson());
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.HMAC_SHA256);
        jws.setKey(key);
        jws.setDoKeyValidation(false); // relaxes the key length requirement

        //签名
        String token = jws.getCompactSerialization();
        return token;
    }

    /**
     * 从Token中获取信息(解析token成UserToken对象)
     *
     * @param token
     * @return
     * @throws Exception
     */
    public UserToken getInfoFromToken(String token) {

        //双重检测，如果请求头中没有token，或者redis里面token过期（退出登录）直接返回
        if (token == "null" || token == null || !redisUtil.hasKey(token)) {
            throw new BusinessException(EnumBusinessError.USER_NOT_LOGIN, "登录过期，请重新登录");
        }
        Key key;
        try {
            key = new HmacKey(Constant.JWT_PRIVATE_KEY.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new BusinessException(EnumBusinessError.UNKNOWN_ERROR, "解析token异常");
        }

        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime()
                .setAllowedClockSkewInSeconds(30)
                .setRequireSubject()
                .setVerificationKey(key)
                .setRelaxVerificationKeyValidation() // relaxes key length requirement
                .build();
        JwtClaims processedClaims;
        try {
            processedClaims = jwtConsumer.processToClaims(token);
        } catch (Exception e) {
            throw new BusinessException(EnumBusinessError.USER_NOT_LOGIN, "登录过期，请重新登录");
        }
        try {
            return new UserToken(
                    processedClaims.getSubject(),
                    processedClaims.getClaimValue(Constant.CONTEXT_NICK_NAME).toString(),
                    processedClaims.getClaimValue(Constant.CONTEXT_IMAGE).toString(),
                    processedClaims.getClaimValue(Constant.CONTEXT_DATE).toString());
        } catch (MalformedClaimException e) {
            throw new BusinessException(EnumBusinessError.PARAMETER_INVALID_ERROR, "请不要尝试伪造token");
        }
    }

    /**
     * 解析token并返回当前对象
     *
     * @return
     */
    public UserToken parseTokenToUserToken() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String token = request.getHeader("Authorization");
        if (token == "null" || StringUtils.isEmpty(token) || !redisUtil.hasKey(token)) {
            throw new BusinessException(EnumBusinessError.USER_NOT_LOGIN, "登录过期，请重新登录");
        }
        return getInfoFromToken(token);
    }

    /**
     * 单单判断是否登录
     *
     * @return
     */
    public String parseTokenToken() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        String token = request.getHeader("Authorization");
        if (StringUtils.isEmpty(token) || "null".equals(token) || !redisUtil.hasKey(token)) {
            return "";
        }
        return token;
    }


    /**
     * 当用户主动退出登录或者关闭浏览器的时候，移除用户token
     *
     * @return
     * @throws BusinessException
     */
    public boolean removeToken() throws BusinessException {
        try {
            String token = parseTokenToken();
            redisUtil.del(token);
        } catch (Exception e) {
            throw new BusinessException(EnumBusinessError.UNKNOWN_ERROR, "移除Token失败，请稍后再试");
        }
        return true;
    }

    /**
     * 存储Token
     *
     * @param token
     * @param telPhone
     * @return
     * @throws BusinessException
     */
    public boolean storageToken(String token, String telPhone) throws BusinessException {
        try {
            redisUtil.set(token, telPhone, 60 * 24);
            return true;
        } catch (Exception e) {
            throw new BusinessException(EnumBusinessError.UNKNOWN_ERROR, "存储令牌失败");
        }
    }

    /**
     * 检查token是否还有效
     *
     * @param token
     * @return
     */
    public boolean inspectionToken(String token) throws BusinessException {
        try {
            if (StringUtils.isEmpty(token)) {
                return false;
            }
            boolean b = redisUtil.hasKey(token);
            if (b) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new BusinessException(EnumBusinessError.USER_NOT_LOGIN, "请登录后再执行此操作");
        }
    }

}
